package cayte.sudoku.crack.comm;

import java.util.Stack;

import cayte.sudoku.crack.MainActivity;
import cayte.sudoku.crack.TableView;
import cayte.sudoku.crack.obj.Gr;
import cayte.sudoku.crack.obj.MarkGr;
import cayte.sudoku.crack.obj.Sudoku;

public class CrackUtil {
	private Stack<Sudoku> sudokus = null;
	private Sudoku rootSudoku = null;
	private Sudoku answerSudoku = null;
	private Sudoku curSudoku = null;
	private TableView table = null;
	private Gr g = null;
	private boolean isStop = false;
	private int count;

	public CrackUtil(TableView table) {
		// TODO Auto-generated constructor stub
		this.sudokus = new Stack<Sudoku>();
		this.table = table;
	}

	public void setRootSudoku(Sudoku sudoku) {
		// TODO Auto-generated method stub
		sudokus.clear();
		rootSudoku = sudoku;
	}

	public Sudoku getAnswerSudoku() {
		return answerSudoku;
	}

	public void crack() {
		// TODO Auto-generated method stub
		if (rootSudoku == null)
			return;

		if (!checkSudoku(rootSudoku)) {
			table.showEnd(MainActivity.State.RESET);
			rootSudoku = null;
			return;
		}

		curSudoku = new Sudoku(rootSudoku);
		//
		initCrack(curSudoku);
		//
		count = 0;
		isStop = false;

		while (true) {
			try {
				System.out.println(isStop);
				if (isStop)
					break;
				curSudoku = crackOne(curSudoku);
				if (curSudoku == null)
					break;
			} catch (CrackException e) {
				// TODO Auto-generated catch block
				table.showEnd(MainActivity.State.END);
				break;
			} catch (Exception e) {
				table.showEnd(MainActivity.State.END);
			}
		}
		if (!isStop) {
			if (checkSudoku(answerSudoku)) {
				table.updateTableView(answerSudoku);
				table.showEnd(MainActivity.State.SUCCESS);
			} else {
				table.showEnd(MainActivity.State.END);
			}
		}
		end();
	}

	public void stop() {
		// TODO Auto-generated method stub
		isStop = true;
	}

	private enum State {
		ERR, ONE, NULL, END;
	}

	private Sudoku crackOne(Sudoku sudoku) throws CrackException {
		// TODO Auto-generated method stub
		State state = State.END;
		MarkGr mark = new MarkGr();

		System.out.println(">>>" + count);
		// System.out.println(sudoku.toDetailed());
		count++;

		for (int r = 0; r < sudoku.grs.length; r++) {
			for (int c = 0; c < sudoku.grs[r].length; c++) {
				g = sudoku.grs[r][c];
				if (!g.isValueNull()) {
					g = null;
					continue;
				}
				if (g.getAnLength() == 0) {
					state = State.ERR;
					break;
				} else if (g.getAnLength() == 1) {
					g.setValue(g.getAn());
					crackGr(r, c, sudoku, g);
					table.setValue(r, c, g);
					state = State.ONE;
					break;
				} else {
					if (g.getAnLength() < mark.length) {
						mark.row = r;
						mark.col = c;
						mark.mark = g.getFirstAn();
						mark.length = g.getAnLength();
						state = State.NULL;
					}
					g = null;
				}
			}
			if (state == State.ONE || state == State.ERR)
				break;
		}

		switch (state) {
		case NULL:// 未解到数字,开始猜解
			if (mark.row == -1 || mark.col == -1)
				throw new CrackException();
			sudoku.setMark(mark);
			sudokus.push(sudoku);
			curSudoku = new Sudoku(sudoku);
			curSudoku.grs[mark.row][mark.col].setValue(mark.mark);
			crackGr(mark.row, mark.col, curSudoku,
					curSudoku.grs[mark.row][mark.col]);
			table.setValue(mark.row, mark.col,
					curSudoku.grs[mark.row][mark.col]);
			return curSudoku;
		case ONE:// 解出一个数字,继续下一个数字
			g = null;
			return curSudoku;
		case ERR:// 出错,返回上一次猜解
			if (sudokus.empty())// 抛出异常,数独不正确
				throw new CrackException();
			curSudoku = sudokus.pop();
			curSudoku.grs[curSudoku.getMark().row][curSudoku.getMark().col]
					.removeAnOne(curSudoku.getMark().mark);
			curSudoku.setMark(null);
			table.updateTableView(curSudoku);
			return curSudoku;
		case END:
			answerSudoku = new Sudoku(curSudoku);
			return null;
		default:
			return null;
		}
	}

	private boolean checkSudoku(Sudoku sudoku) {
		// TODO Auto-generated method stub
		if (sudoku == null)
			return false;
		for (int r = 0; r < sudoku.grs.length; r++) {
			for (int c = 0; c < sudoku.grs[r].length; c++) {
				if (!sudoku.grs[r][c].isValueNull()) {
					int value = sudoku.grs[r][c].getValue();
					// 横
					for (int i = 0; i < sudoku.grs[r].length; i++) {
						if (i != c)
							if (sudoku.grs[r][i].getValue() == value)
								return false;
					}
					// 竖
					for (int i = 0; i < sudoku.grs.length; i++) {
						if (i != r)
							if (sudoku.grs[i][c].getValue() == value)
								return false;
					}
					// 九格
					int x = r - r % 3;
					int y = c - c % 3;
					for (int i = x; i < (x + 3); i++) {
						for (int j = y; j < (y + 3); j++) {
							if (i != r && j != c)
								if (sudoku.grs[i][j].getValue() == value)
									return false;
						}
					}
				}
			}
		}
		return true;
	}

	private void initCrack(Sudoku sudoku) {
		// TODO Auto-generated method stub
		for (int r = 0; r < sudoku.grs.length; r++) {
			for (int c = 0; c < sudoku.grs[r].length; c++) {
				g = sudoku.grs[r][c];
				crackGr(r, c, sudoku, g);
				g = null;
			}
		}
	}

	/*** 如果一个格子有数字,处理和这个格子相关的其他格子,横,竖,九格 ***/
	private void crackGr(int r, int c, Sudoku sudoku, Gr g) {
		// TODO Auto-generated method stub
		if (sudoku == null || g == null || g.isValueNull())
			return;
		// 横
		for (int i = 0; i < sudoku.grs[r].length; i++) {
			if (i != c)
				sudoku.grs[r][i].removeAnOne(g.getValue());
		}
		// 竖
		for (int i = 0; i < sudoku.grs.length; i++) {
			if (i != r)
				sudoku.grs[i][c].removeAnOne(g.getValue());
		}
		// 九格
		int x = r - r % 3;
		int y = c - c % 3;
		for (int i = x; i < (x + 3); i++) {
			for (int j = y; j < (y + 3); j++) {
				if (i != r && j != c)
					sudoku.grs[i][j].removeAnOne(g.getValue());
			}
		}
	}

	private void end() {
		// TODO Auto-generated method stub
		g = null;
		curSudoku = null;
		rootSudoku = null;
		sudokus.clear();
	}

	public class CrackException extends Exception {
		private static final long serialVersionUID = -6301404172979982608L;
	}
}
